Skip to content

k3s follow-up: channels via INSTALL_K3S_CHANNEL, *.yml data-dir (after #8) - #9

Merged
tekn0ir merged 3 commits into
mainfrom
fix/k3s-version-channel
Oct 12, 2026
Merged

tekn0ir merged 3 commits into
mainfrom
fix/k3s-version-channel

Conversation

@tekn0ir

@tekn0ir tekn0ir commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #8 (branch fix/k3s-ca-root-keycertsign, head 1d61656). Rebase and retarget to main after #8 merges; until then the diff against main includes #8.

  • Channel names (stable, latest, testing, vMAJ.MIN) given as the version are moved to INSTALL_K3S_CHANNEL and INSTALL_K3S_VERSION is unset before the installer runs (the k3s installer treats the version as an exact release). All policy checks stay: keep-installed by default, refuse downgrades and multi-minor unless forced, cgroup handling with an unknown target. Server and agent paths.
  • tnctl bootstrap --k3s-version accepts and renders channels (stable, latest, testing, v1.34) as INSTALL_K3S_CHANNEL.
  • tn_ca_resolve_datadir / tn_ca_datadir_consistent read config.yaml.d/*.yaml and *.yml, suffix case-insensitive (like k3s), lexical order, last wins. The cgroup drop-in lookup is case-insensitive too.

Tests: new unit cases (channel to INSTALL_K3S_CHANNEL, exact stays exact, a fake installer asserting what it receives, dash, .yml/.YAML drop-ins); go test ./... green; harness --suite install,rerun with v1.34.11 and v1.36.5 (bs-o111) and TN_K3S_CA_LIB/TN_K3S_INSTALL_LIB set: 60 PASS, 0 mismatch.

Review fixes (8ba02da)

  • With TN_K3S_UPGRADE=true, a channel no longer skips the downgrade and multi-minor refusals. vMAJ.MIN channel: compared by major.minor with the installed k3s; a lower minor or another major is always refused (force does not help), a jump of more than one minor needs TN_K3S_FORCE=true, same minor or +1 is allowed. stable/latest/testing: resolved like install.sh (curl -w '%{url_effective}' -L -s -S https://update.k3s.io/v1-release/channels/<ch>, last path segment), pinned as the exact release and checked in full; if it cannot be resolved the upgrade is refused unless TN_K3S_FORCE=true (then it goes on unchecked, with a warning).
  • A channel in INSTALL_K3S_VERSION no longer overwrites an explicitly set, different INSTALL_K3S_CHANNEL: refused as conflicting.
  • Tests: stub curl for update.k3s.io; with v1.34.11 installed: v1.33 refused (also with force), v1.36 refused without force, v1.35 and v1.34 allowed, stable resolving to v1.36.5 refused without force, unresolvable stable refused without force. go test ./... green; harness --suite install,rerun (v1.34.11, v1.36.5, bs-o111, both libs set): 30 PASS, 0 mismatch.
  • bootstrap-device fix/k3s-followup-vendor (46cbdcd): re-vendored, header comment on channel pins; check-vendored, check-assembly, check-setup pass.

🤖 Generated with Claude Code

…-ins may be *.yml in any case

The k3s installer reads INSTALL_K3S_VERSION as an exact release, so stable, latest, testing or
v1.34 there would fail. The install library moves a channel name to INSTALL_K3S_CHANNEL and unsets
the version before any policy check; tnctl bootstrap --k3s-version renders channels the same way.

tn_ca_resolve_datadir and tn_ca_datadir_consistent read config.yaml.d/*.yaml and *.yml, suffix
case-insensitive like k3s, in lexical order, last wins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tekn0ir

tekn0ir commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

Review: c4adaf8 against 1d61656 (and bootstrap-device 6ec99e5)

Verdict: changes requested (one must-fix).

Must fix: an upgrade to a vMAJ.MIN channel skips the downgrade and multi-minor checks

With TN_K3S_UPGRADE=true and INSTALL_K3S_VERSION=v1.33 (or the new tnctl bootstrap --k3s-version v1.33 --k3s-upgrade), tn_inst_normalize_pin turns the value into INSTALL_K3S_CHANNEL=v1.33. tn_inst_plan then finds no exact target (tn_inst_cmp fails), prints only the "not an exact release" warning and hands the channel to the installer.

I ran this under dash with a fake installer and k3s v1.34.11+k3s1 installed:

request (TN_K3S_UPGRADE=true) 1d61656: installer gets c4adaf8: installer gets
INSTALL_K3S_VERSION=v1.33 VERSION=v1.33, which 404s on GitHub, so nothing is replaced CHANNEL=v1.33: a downgrade, and the datastore has already been migrated
INSTALL_K3S_VERSION=v1.36 VERSION=v1.36, which 404s CHANNEL=v1.36: a two-minor jump without TN_K3S_FORCE

At 1d61656 these requests failed safe by accident. Now they go through, and the bootstrap-device re-vendor (6ec99e5) inherits the same path, because TN_K3S_VERSION=v1.33 is exported as INSTALL_K3S_VERSION.

The channel name gives the minor version exactly; tn_inst_cgroup already reads it that way. In the upgrade branch of tn_inst_plan, when INSTALL_K3S_CHANNEL is vMAJ.MIN:

  • refuse a lower minor or a different major (downgrade);
  • refuse more than one minor above the installed one, unless TN_K3S_FORCE=true.

A same-minor channel is fine, because channels only move forward.

Recommended, not blocking: also resolve stable/latest/testing before the checks, the way install.sh does (curl -w '%{url_effective}' -L ${INSTALL_K3S_CHANNEL_URL:-https://update.k3s.io/v1-release/channels}/$ch, then take the basename). That gives channels the full checks. If it cannot be resolved, keep today's warning. Strictly, INSTALL_K3S_VERSION=stable with an upgrade also used to fail safe (404). But INSTALL_K3S_CHANNEL=stable and --k3s-version stable already behaved like today at 1d61656, so I am not counting it as a regression.

Verified OK

  • Channels go to INSTALL_K3S_CHANNEL (dash, fake installer, no k3s installed): stable, latest, testing and v1.34 given as INSTALL_K3S_VERSION reach the installer as INSTALL_K3S_CHANNEL=<value>, with INSTALL_K3S_VERSION unset.

  • Exact versions are unchanged: v1.36.5+k3s1 and v1.36.5-rc1+k3s1 pass through as they did at 1d61656. A plain INSTALL_K3S_CHANNEL=latest is also unchanged.

  • Keep-installed (no TN_K3S_UPGRADE): an exact pin, a channel or the same release keeps the installed v1.34.11+k3s1, as at 1d61656.

  • Exact-pin upgrades: a downgrade (v1.33.1) is refused, a jump to v1.36.5 is refused, v1.35.2 is allowed, and FORCE allows v1.36.5. All match 1d61656.

  • cgroup v1, upgrade from v1.30.5:

    • channel v1.31: no INSTALL_K3S_EXEC flag, which is correct because v1.31 does not need it on its first start;
    • channel v1.36: --kubelet-arg=fail-cgroupv1=false is added.
  • Agent (local fake server publishing v1.34.11+k3s1):

    • a channel or vMAJ.MIN flag pin installs the server's exact release;
    • an exact pin newer than the server is refused, and an older one is installed;
    • with the server unreachable, a channel pin is refused and an exact pin is installed with a warning;
    • an agent upgrade follows the server.

    All identical to 1d61656.

  • Data-dir drop-ins: tn_ca_config_dropins matches k3s v1.36.5 pkg/configfilearg.dotDFiles, which uses os.ReadDir, so the list is in byte order, matched with HasSuffixI(".yaml", ".yml"). Under dash, 10-b.yml, 15-e.Yml, 20-c.YAML, Z-f.yaml and a-g.yaml came out in k3s order, last one wins (/g). 30-d.txt and a directory named sub.yaml are skipped.

  • go test ./... passes. The three vendored files in bootstrap-device 6ec99e5 are identical to c4adaf8.

Notes (not blocking, already true at 1d61656)

  • Symlinked drop-ins: k3s reads a drop-in that is a symlink (IsDir() is false for a symlink), but find -type f skips it. -type f → \( -type f -o -type l \) (or -xtype f) would match k3s.
  • Pin source in bootstrap-device: 00_tn_header.sh sets TN_K3S_PIN_SOURCE from INSTALL_K3S_VERSION only. The cli template also counts INSTALL_K3S_CHANNEL. The header comment there still says config.yaml.d/*.yaml.
  • Conflicting values: when INSTALL_K3S_VERSION is a channel and INSTALL_K3S_CHANNEL is also set, the version wins and overwrites the channel. That is reasonable, but it is worth a line in the docs.

🤖 Generated with Claude Code

…n INSTALL_K3S_VERSION never overwrites INSTALL_K3S_CHANNEL

With TN_K3S_UPGRADE=true a vMAJ.MIN channel skipped the downgrade and multi-minor refusals. Now a minor channel is compared by major.minor (lower or other major: refused; more than +1: needs TN_K3S_FORCE), stable/latest/testing is resolved at update.k3s.io and checked as an exact release, and an unresolvable channel is refused unless TN_K3S_FORCE=true.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tekn0ir

tekn0ir commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

Delta review: c4adaf8..8ba02da (cli) and 6ec99e5..46cbdcd (bootstrap-device)

Verdict: approve. No must-fix. This delta adds no path that proceeds where 1d61656 refused. It also never installs a release other than the one 1d61656 (or the k3s installer) would have picked. Every behaviour change is a new refusal, and a refusal happens in tn_inst_prepare, which runs before tn_k3s_ca_setup in both server templates. A refused run therefore leaves the device untouched.

How it was checked

I used a separate harness under dash with set -e and noglob. It runs a fake k3s --version, a stub curl (a redirect from update.k3s.io, or exit 6 when the channel cannot be resolved, plus /static/teknoir/k3s-version for agents) and tn_inst_prepare. Each case ran against the libraries at 1d61656, c4adaf8 and 8ba02da.

Channel cases with v1.34.11+k3s1 installed (the channel given as INSTALL_K3S_VERSION or as INSTALL_K3S_CHANNEL; results were identical either way):

request TN_K3S_UPGRADE=true + TN_K3S_FORCE=true no upgrade
v1.33 refused (downgrade) refused keep v1.34.11
v2.0 refused refused keep
v1.34 upgrade (channel v1.34) upgrade keep
v1.35 upgrade (channel v1.35) upgrade keep
v1.36 refused (more than one minor) upgrade, warns keep
stable/latest/testing → v1.35.9 pinned v1.35.9+k3s1, upgrade same keep, no call to update.k3s.io
… → v1.36.5 refused (more than one minor) pinned v1.36.5, warns keep
… → v1.34.11 same same keep
… → v1.33.9 refused (downgrade) refused keep
… cannot be resolved refused, "Nothing was changed" channel kept, unchecked, warns keep
no pin or channel (default stable) resolved and pinned / refused when it cannot be resolved

The %2B in the redirect decodes to +. Once pinned, INSTALL_K3S_CHANNEL is unset, so the installer is given only INSTALL_K3S_VERSION.

cgroup v1 with v1.30.14 installed:

  • stable → v1.31.9: deferred, EXEC unchanged.
  • stable → v1.36.5 with force: EFFECTIVE=v1.36.5 and --kubelet-arg=fail-cgroupv1=false added to EXEC. Resolving the channel makes this more precise than before.
  • stable that cannot be resolved, with force: the flag goes into EXEC, as at 1d61656.
  • channel v1.31: deferred, no flag.

Exact versions, unchanged against 1d61656: 336 server cases with 0 differences in rc, action, version, channel, effective, EXEC or deferred. The matrix crossed:

  • installed: none, 1.30.14, 1.33.2, 1.34.11, 1.35.9, 1.36.5+k3s1 and +k3s2
  • pins: 1.33.2, 1.34.11 (with and without +k3s1), 1.35.9, 1.36.5, 1.36.6
  • with and without TN_K3S_UPGRADE, with and without TN_K3S_FORCE
  • cgroup v1 and v2

Agent path, unchanged against 1d61656: 288 cases with 0 differences. The matrix crossed:

  • installed: none, 1.34.11, 1.35.9
  • server: unreachable, 1.34.11, 1.35.9, 1.36.5
  • pin: none, 1.36.5, 1.34.11
  • TN_K3S_PIN_SOURCE: default or flag
  • with and without TN_K3S_UPGRADE, with and without TN_K3S_FORCE

The one new agent behaviour: TN_K3S_UPGRADE + TN_K3S_FORCE + an unreachable server + a channel now pins the release that stable resolves to, where it used to pass the channel on. Both install the same release.

Other checks:

  • go test ./... passes.
  • bootstrap-device check-vendored.sh --cli: all 3 libraries and the 16 harness files are identical to 8ba02da.
  • Both branch heads match origin.

Notes (non-blocking; follow-ups worth considering)

  1. The conflict refusal also fires on a plain re-run and on a fresh install, not only on an upgrade. A script made with tnctl bootstrap --k3s-version stable bakes in INSTALL_K3S_CHANNEL=stable. Running it as INSTALL_K3S_VERSION=v1.35 sh bootstrap.sh is now refused ("set only one of them"), even with no upgrade. At 1d61656 and c4adaf8, that re-run kept k3s and finished the repair. The person set only one of the two; the template set the other. Two options: let the env INSTALL_K3S_VERSION win over the template's channel, or refuse only when the channel is actually used (fresh install or upgrade). The device is not damaged (the refusal comes before the CA step), but the repair is blocked.
  2. A non-exact pin with TN_K3S_UPGRADE has no override any more. For example, v1.35.0-rc1+k3s1 is now refused even with TN_K3S_FORCE=true. 1d61656 warned and went on. Consider allowing it, unchecked, with force, the same way an unresolvable channel is allowed.
  3. The resolve curl has no timeout and no wget fallback.
    • Add --connect-timeout/--max-time, as tn_inst_http_get does (10 s). On a network that silently drops packets, curl can otherwise hang for minutes.
    • Without curl (wget-only hosts, which install.sh supports), the channel is refused unless TN_K3S_FORCE=true.
  4. INSTALL_K3S_CHANNEL_URL is ignored. install.sh resolves ${INSTALL_K3S_CHANNEL_URL:-https://update.k3s.io/v1-release/channels}/$channel. With a mirror, the library resolves against a different server than the installer would use, and refuses when update.k3s.io cannot be reached.
  5. A minor channel is only compared by major.minor. If an exact pin left the device ahead of the channel head (for example v1.34.12 installed while channel v1.34 still points to v1.34.11), the channel can resolve to a lower patch and the upgrade is allowed. update.k3s.io resolves v1.34 the same way it resolves stable. Resolve it first and fall back to the minor comparison only when it cannot be resolved.
  6. bootstrap-device: I agree with leaving TN_K3S_PIN_SOURCE as it is. Its templates never set INSTALL_K3S_CHANNEL, and the header comment matches what the vendored library does.

Nothing was merged. fix/k3s-ca-root-keycertsign and the VM rig were not touched.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tekn0ir
tekn0ir marked this pull request as ready for review October 12, 2026 08:39
@tekn0ir
tekn0ir merged commit 47d39cd into main Oct 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant